package com.heima.search.service.impl;

import com.heima.article.client.ArticleFeign;
import com.heima.common.enums.AppHttpCodeEnum;
import com.heima.common.exception.LeadException;
import com.heima.common.threadlocal.UserThreadLocalUtils;
import com.heima.common.util.JsonUtils;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.search.dtos.UserSearchDto;
import com.heima.search.interceptors.UserInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.validation.constraints.NotBlank;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class ArticleSearchService {
    @Autowired
    private ArticleFeign articleFeign;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    private String indexName = "app_info_article";

    //远程调用article服务，查询文章列表
    public List<ArticleDto> findArticleByPage(int page, int size) {
        List<ArticleDto> articleDtoList = articleFeign.findArticleByPage(page, size);
        return articleDtoList;
    }

    //将数据导入ES
    public void importEs(List<ArticleDto> articleDtos) {
        BulkRequest bulkRequest = new BulkRequest();
        for (ArticleDto articleDto : articleDtos) {
            // 构建map，重新构造数据
            Map<String, Object> map = new HashMap<>();
            map.put("id", articleDto.getId());
            map.put("publishTime", articleDto.getPublishTime());
            map.put("layout", articleDto.getLayout());
            map.put("images", articleDto.getImages());
            map.put("staticUrl", articleDto.getStaticUrl());
            map.put("authorId", articleDto.getAuthorId());
            map.put("authorName", articleDto.getAuthorName());
            map.put("title", articleDto.getTitle());
            map.put("content", articleDto.getContent());
            //map转json
            String jsonStr = JsonUtils.toString(map);

            IndexRequest indexRequest = new IndexRequest(indexName);
            indexRequest.id(articleDto.getId().toString());
            indexRequest.source(jsonStr, XContentType.JSON);
            bulkRequest.add(indexRequest);
        }
        try {
            restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        } catch (Exception e) {
            log.error("批量导入Es失败");
            e.printStackTrace();
            throw new LeadException(AppHttpCodeEnum.SERVER_ERROR);
        }

    }

    @Autowired
    private StringRedisTemplate redisTemplate;

    //关键词搜索
    public List<ArticleDto> search(UserSearchDto dto) {
        // 用户输入关键词
        String key = dto.getSearchWords();
        // 创建索引请求封装对象
        SearchRequest searchRequest = new SearchRequest(indexName);
        // 查询条件构造器
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        // 创造bool组合查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(
                QueryBuilders.queryStringQuery(key).field("title").field("content").defaultOperator(Operator.OR)
        );
        // 搜索
        sourceBuilder.query(boolQueryBuilder);
        // 排序
        sourceBuilder.sort("publishTime", SortOrder.DESC);
        //分页
        int index = dto.getFromIndex();
        sourceBuilder.from(index);
        sourceBuilder.size(dto.getPageSize());
        searchRequest.source(sourceBuilder);

        //设置高亮  title
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title");
        highlightBuilder.preTags("<font style='color: pink; font-size: inherit;'>");
        highlightBuilder.postTags("</font>");
        sourceBuilder.highlighter(highlightBuilder);
        List<ArticleDto> list = new ArrayList<>();
        // 使用api进行关键词查询
        try {
            SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits hits = response.getHits();
            for (SearchHit hit : hits) {
                String jsonStr = hit.getSourceAsString();
                // 将json转对象
                ArticleDto articleDto = JsonUtils.toBean(jsonStr, ArticleDto.class);
                //处理高亮
                if (hit.getHighlightFields() != null && hit.getHighlightFields().size() > 0) {
                    Text[] titles = hit.getHighlightFields().get("title").getFragments();
                    String title = StringUtils.join(titles);
                    // 高亮标题
                    articleDto.setTitle(title);
                }
                list.add(articleDto);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 如果搜索到关键词，size>0，保存关键词
        if (!CollectionUtils.isEmpty(list)) {
            Integer userId = UserThreadLocalUtils.getUserId();
            saveSearchWord(key, userId, dto.getEquipmentId());
        }

        return list;

    }

    /**
     * 保存搜索记录
     *
     * @param key         用户输入的关键字
     * @param userId
     * @param equipmentId
     */
    @Async
    public void saveSearchWord(String key, Integer userId, String equipmentId) {

        String redisKey = getRedisKey(userId, equipmentId);
        // Redis  key的搜索规约
        redisTemplate.opsForZSet().incrementScore(redisKey, key, 1);
        // 有效期存储7天
        redisTemplate.expire(key, 7, TimeUnit.DAYS);
    }

    private String PRE_FIX = "ld:search:word:";

    private String getRedisKey(Integer userId, String equipmentId) {
        if (userId != null || userId != 0) {
            return PRE_FIX + userId;
        } else {
            return PRE_FIX + equipmentId;
        }

    }

    /**
     * 获取用户搜索关键词
     * @param dto
     * @return
     */
    public Set<String> loadHistory(UserSearchDto dto) {
        Integer userId = UserThreadLocalUtils.getUserId();
        String equipmentId = dto.getEquipmentId();
        String redisKey = getRedisKey(userId, equipmentId);
        Set<String> set = redisTemplate.opsForZSet().reverseRange(redisKey, 0, -1);
        return set;
    }

    /**
     * 删除 用户搜索记录
     * @param dto
     */
    public void delHistory(UserSearchDto dto) {
        Integer userId = UserThreadLocalUtils.getUserId();
        String equipmentId = dto.getEquipmentId();
        String redisKey = getRedisKey(userId, equipmentId);
        redisTemplate.opsForZSet().remove(redisKey,dto.getSearchWords());
    }
}
